package com.yhq.basic.design.singleton;

import lombok.SneakyThrows;
import org.omg.SendingContext.RunTime;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;

/**
 * @author hqyin
 * @date 2023/2/3 10:15 上午
 */
public class DemoTest {
    @SneakyThrows
    public static void main(String[] args) {
        //饿汉式一，静态常量初始化Instance
        //testHungrySingletonByStaticInstance();
        //System.out.println("\n=================================\n");
        //饿汉式二，静态代码块初始化Instance
        //testHungrySingletonByStaticCodeBlock();
        //System.out.println("\n=================================\n");
        //饿汉式三，枚举，极力推荐，非常安全无法破解
        //testHungrySingletonByEnums();

        //System.out.println("\n=================================\n");
        //懒汉式一，方法加synchronized
        //testLazySingletonByMethodLock();

        //System.out.println("\n=================================\n");
        //懒汉式二，双重检测
        //testLazySingletonByDualCheck();

        //System.out.println("\n=================================\n");
        //懒汉式三，静态内部类
        //testLazySingletonByStaticInternalClass();

        //防止序列化和反射破解单例
        testLazySingletonNotDestroy();


    }

    private static void testLazySingletonNotDestroy() {
        Singleton7 obj1 = Singleton7.getInstance();
        Singleton7 obj2 = Singleton7.getInstance();
        System.out.println(String.format("防止序列化和反射破解单例,\nobj1:%s,\nobj2:%s,\nobj1==obj2:%s", obj1, obj2, obj1 == obj2));

        //序列化破解单例
        serializingToSingleton("(防止序列化破解单例)", obj1);

        //反射破解单例
        //reflectToSingleton("(防止反射破解单例)",Singleton7.class);
    }

    private static void testLazySingletonByStaticInternalClass() {
        Singleton6 obj1 = Singleton6.getInstance();
        Singleton6 obj2 = Singleton6.getInstance();
        System.out.println(String.format("懒汉式三，静态内部类,\nobj1:%s,\nobj2:%s,\nobj1==obj2:%s", obj1, obj2, obj1 == obj2));

        //反射破解单例
        reflectToSingleton("(懒汉式三，静态内部类)", Singleton6.class);

        //序列化破解单例
        serializingToSingleton("(懒汉式三，静态内部类)", obj1);
    }

    private static void testLazySingletonByDualCheck() {
        Singleton5 obj1 = Singleton5.getInstance();
        Singleton5 obj2 = Singleton5.getInstance();
        System.out.println(String.format("懒汉式二，双重检测,\nobj1:%s,\nobj2:%s,\nobj1==obj2:%s", obj1, obj2, obj1 == obj2));

        //反射破解单例
        reflectToSingleton("(懒汉式二，双重检测)", Singleton5.class);

        //序列化破解单例
        serializingToSingleton("(懒汉式二，双重检测)", obj1);
    }

    private static void testLazySingletonByMethodLock() {
        Singleton4 obj1 = Singleton4.getInstance();
        Singleton4 obj2 = Singleton4.getInstance();
        System.out.println(String.format("懒汉式一，方法加synchronized,\nobj1:%s,\nobj2:%s,\nobj1==obj2:%s", obj1, obj2, obj1 == obj2));

        //反射破解单例
        reflectToSingleton("(懒汉式一，方法加synchronized)", Singleton4.class);

        //序列化破解单例
        serializingToSingleton("(懒汉式一，方法加synchronized)", obj1);
    }

    private static void testHungrySingletonByEnums() {
        Singleton3 obj1 = Singleton3.INSTANCE;
        Singleton3 obj2 = Singleton3.INSTANCE;
        System.out.println(String.format("饿汉式三，枚举初始化Instance,\nobj1:%s,\nobj2:%s,\nobj1==obj2:%s", obj1, obj2, obj1 == obj2));
    }

    private static void testHungrySingletonByStaticCodeBlock() {
        Singleton2 obj1 = Singleton2.getInstance();
        Singleton2 obj2 = Singleton2.getInstance();
        System.out.println(String.format("饿汉式二，静态代码块初始化Instance,\nobj1:%s,\nobj2:%s,\nobj1==obj2:%s", obj1, obj2, obj1 == obj2));

        //反射破解单例
        reflectToSingleton("(饿汉式二，静态代码块初始化Instance)", Singleton2.class);

        //序列化破解单例
        serializingToSingleton("(饿汉式二，静态代码块初始化Instance)", obj1);
    }

    private static void testHungrySingletonByStaticInstance() {
        Singleton1 obj1 = Singleton1.getInstance();
        Singleton1 obj2 = Singleton1.getInstance();
        System.out.println(String.format("饿汉式一:静态常量初始化Instance,\nobj1:%s,\nobj2:%s,\nobj1==obj2:%s", obj1, obj2, obj1 == obj2));

        //反射破解单例
        reflectToSingleton("(饿汉式一:静态常量初始化Instance)", Singleton1.class);

        //序列化破解单例
        serializingToSingleton("(饿汉式一:静态常量初始化Instance)", obj1);
    }

    @SneakyThrows
    private static void serializingToSingleton(String type, Object obj1) {
        String fileName = DemoTest.class.getResource("/").getPath() + obj1.getClass().getSimpleName();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fileName));
        out.writeObject(obj1);
        out.flush();
        out.close();
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName));
        Object obj2 = in.readObject();
        in.close();
        System.out.println(String.format("序列化破解单例%s,\nobj1:%s,\nobj2:%s,\nobj1==obj2:%s", type, obj1, obj2, obj1 == obj2));
    }

    @SneakyThrows
    private static void reflectToSingleton(String type, Class<? extends Object> clazz) {
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Object obj1 = constructor.newInstance();
        Object obj2 = constructor.newInstance();
        System.out.println(String.format("反射破解单例%s,\nobj1:%s,\nobj2:%s,\nobj1==obj2:%s", type, obj1, obj2, obj1 == obj2));
    }
}
